import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

public class Main {
    static List<Flow> flowList;
    static List<Port> portList;
    static int portNum;
    static Queue<Flow> dispatch;
    static LinkedList<Schedule> schedulesList ;
    private static final int maxQueue = 30;
    private static final int  maxDispatchFactor = 20;
    static  int maxDispatch;
    static  double baseLine;
    static int abandonTime = 0;
    static int runrunTime = 0;
    private  static  int maxLastNum;
    private  static  final  int powNum = 2;
    public static void main(String[] args) throws IOException {
        String dataFilePath = "../data";
        File dataFiles = new File(dataFilePath);
        long beginAll = System.currentTimeMillis();
        // 遍历data文件夹
        for (String dataFile : dataFiles.list()) {
            long begin = System.currentTimeMillis();
            String filePath = dataFilePath +"/"+ dataFile;
            System.out.println("开始处理"+filePath);

            // 选出最优的时间
            int bestTime = Integer.MAX_VALUE;
            int bestRunTime = 0;
            int bestAbandonTime =0;
            // 最优调度
            LinkedList<Schedule> bestSchedulesList =new LinkedList<>();
            // 最优调度参数
            int best1 = 0,best2 = 0, best3 = 0;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 2; j++) {
                    for (int k = 0; k < 2; k++) {
                        abandonTime = 0;
                        runrunTime = 0;
                        schedulesList = new LinkedList<>();
                        method(filePath,j,i,k);
                        int allTime =  runrunTime+abandonTime;
                        if(allTime < bestTime){
                            bestTime = allTime;
                            bestSchedulesList = schedulesList;
                            bestRunTime = runrunTime;
                            bestAbandonTime = abandonTime;
                            best1 = i;
                            best2 = j;
                            best3 = k;
                        }
                    }
                }
            }

            System.out.println("=====");
            System.out.println(best1+"   " +best2+"   "+ best3);
            System.out.println("bestRunTime:    "+bestRunTime);
            System.out.println("bestAbandonTime:    "+bestAbandonTime);
            System.out.println("bestTime:    "+bestTime);
            System.out.println("=====");

            // 写结果
            String fileName =filePath+ "/result.txt";;
            File file = new File(fileName);
            if(file.exists()){
                file.delete();
            }
            file.createNewFile();
            Path path = Paths.get(fileName);
            //追加写模式
            BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.APPEND);
            for (Schedule schedule : bestSchedulesList) {
                writer.write(schedule.flowId+","+schedule.portId+","+schedule.time+"\r\n");
            }
            writer.close();


            long end = System.currentTimeMillis();
            System.out.println("耗时：    "+ (end - begin)/1000);

        }
        long allEnd = System.currentTimeMillis();
        System.out.println("总耗时：    "+ ( allEnd-beginAll)/1000);
    }

    private static  void chooseDispatch(int type){
        switch(type){
            case 0:
                dispatch = new PriorityQueue<>(((o1, o2) -> {
                    if ((o1.bandwidth * o1.spendTime) == (o2.bandwidth * o2.spendTime)) {
                        return o1.bandwidth - o2.bandwidth;
                    }
                    return (o1.bandwidth * o1.spendTime) - (o2.bandwidth * o2.spendTime);
                }));
                break;
            case 1:
                dispatch = new PriorityQueue<>((Comparator.comparingInt(o -> o.bandwidth / (o.spendTime))));
                break;
            default :
                dispatch = new PriorityQueue<>(((o1, o2) -> {
                    if (o1.bandwidth == o2.bandwidth) {
                        return o2.spendTime - o1.spendTime;
                    }
                    return o1.bandwidth - o2.bandwidth;
                }));
                break;
        }
    }


    static void method(String filePath, int  choosePortType, int chooseDispatchType, int choose) throws IOException {
        chooseDispatch(chooseDispatchType);
        // 读取文件，获取相关基本信息
        flowList = getFlowList(filePath);
        portList = getPortList(filePath);
        portNum = portList.size();
        maxDispatch = portNum * maxDispatchFactor;

        Collections.sort(portList, ((o1, o2) -> o2.bandwidth - o1.bandwidth));
        Port maxPort = portList.get(0);


        // 指定一个标准线，超过这个标准线的  需要一直堆在调度区不能执行也不能丢弃
        maxLastNum =  maxDispatch+portNum*15;
        Collections.sort(flowList,(Comparator.comparingInt(o -> (int) (o.spendTime *  Math.pow(Math.log(o.bandwidth) ,powNum)  ))));
        Flow baseFlow = flowList.get(flowList.size() - maxLastNum);
        baseLine = baseFlow.spendTime * Math.pow(Math.log(baseFlow.bandwidth) ,powNum);

        Collections.sort(flowList,((o1, o2) -> {
            if(o1.startTime == o2.startTime){
                if(o1.spendTime == o1.spendTime){
                    return  o1.bandwidth - o2.bandwidth;
                }
                return o1.spendTime - o1.spendTime;
            }
            return o1.startTime - o2.startTime;
        }));

        int time = 0;
        int flowIndex = 0;
        while(flowIndex < flowList.size()){
            Flow flow= flowList.get(flowIndex);
            // 查看每个端口的队列是否有任务运行结束，更新port的queue的大小
            for (Port port : portList) {
                //  结束运行中的flow
                if(!port.scheduleQueue.endTimeMap.isEmpty()){
                    Integer firstEndTime = port.scheduleQueue.endTimeMap.firstEntry().getKey();
                    if( firstEndTime <= time){
                        TreeMap<Integer, List<Flow>> endTimeMap = port.scheduleQueue.endTimeMap;
                        List<Flow> flows = endTimeMap.firstEntry().getValue();
                        for (Flow flow1 : flows) {
                            port.scheduleQueue.runEndBandwidth += flow1.bandwidth;
                        }
                        port.scheduleQueue.endTimeMap.remove(firstEndTime);
                    }
                }

                // 把正在排队的加入运行
                while(port.scheduleQueue.size>0 &&port.scheduleQueue.runEndBandwidth >= port.scheduleQueue.queue.get(0).bandwidth){
                    port.scheduleQueue.runEndBandwidth -=  port.scheduleQueue.queue.get(0).bandwidth;
                    Flow remove = port.scheduleQueue.queue.remove(0);

                    List<Flow> flows = port.scheduleQueue.endTimeMap.getOrDefault(time + remove.spendTime, new ArrayList<>());
                    flows.add(remove);
                    port.scheduleQueue.endTimeMap.put(time + remove.spendTime,flows);

                    port.scheduleQueue.size--;
                }
            }

            // 把任务加到分配区
            while (flowIndex<flowList.size()-1&& flow.startTime <= time){
                dispatch.add(flow);
                flowIndex++;
                flow = flowList.get(flowIndex);
            }
            if( flow.startTime <= time){
                dispatch.add(flow);
                flowIndex++;
            }
            if(dispatch.size() == 0) {
                time++;
                continue;
            }


            //  时间长，带宽大的不要放入排队区，到了最后再放入排队区
            PriorityQueue<Flow> list = new PriorityQueue<>((Comparator.comparingDouble(o -> o.spendTime * Math.pow(Math.log(o.bandwidth), powNum))));

            // 是否有可以立即进入运行状态的
            boolean stay = false;
            while(!stay && dispatch.size()>0){
                stay = true;
                Flow readyScheduleFlow = dispatch.peek();

                // 如果当前流 时间带宽 综合数值 需要放在最后执行 则加入list 并跳过
                if(readyScheduleFlow.spendTime * Math.pow(Math.log(readyScheduleFlow.bandwidth) ,powNum) >= baseLine){
                    list.add(readyScheduleFlow);
                    dispatch.remove(readyScheduleFlow);
                    stay = false;
                    continue;
                }

//                // 为readyScheduleFlow 选择一个合适的端口立即开始运行
//                int minBandWidth  = Integer.MAX_VALUE;
//                Port choosePort = null;
//                for (Port port : portList) {
//                    if(port.scheduleQueue.runEndBandwidth >= readyScheduleFlow.bandwidth && port.scheduleQueue.size ==0){
//                        if( port.scheduleQueue.runEndBandwidth - readyScheduleFlow.bandwidth < minBandWidth){
//                            minBandWidth = port.scheduleQueue.runEndBandwidth - readyScheduleFlow.bandwidth;
//                            choosePort = port;
//                        }
//                    }
//                }
//                // 如果找到了，则安排进入运行
//                if( choosePort != null){
//                    choosePort.scheduleQueue.runEndBandwidth -= readyScheduleFlow.bandwidth;
//                    choosePort.scheduleQueue.running.add(readyScheduleFlow);
//                    List<Flow> flows = choosePort.scheduleQueue.endTimeMap.getOrDefault(time + readyScheduleFlow.spendTime, new ArrayList<>());
//                    flows.add(readyScheduleFlow);
//                    choosePort.scheduleQueue.endTimeMap.put(time + readyScheduleFlow.spendTime,flows);
//                    schedulesList.add(new Schedule(readyScheduleFlow.id,  choosePort.id, time));
//                    dispatch.remove(readyScheduleFlow);
//                    stay =false;
//                }
                // 为readyScheduleFlow 选择一个合适的端口立即开始运行
                for (int size = portList.size()-1; size >= 0; size--) {
                    Port port = portList.get(size);
                    if(port.scheduleQueue.runEndBandwidth >= readyScheduleFlow.bandwidth && port.scheduleQueue.size ==0){
                        port.scheduleQueue.runEndBandwidth -= readyScheduleFlow.bandwidth;
                        List<Flow> flows = port.scheduleQueue.endTimeMap.getOrDefault(time + readyScheduleFlow.spendTime, new ArrayList<>());
                        flows.add(readyScheduleFlow);
                        port.scheduleQueue.endTimeMap.put(time + readyScheduleFlow.spendTime,flows);
                        schedulesList.add(new Schedule(readyScheduleFlow.id,  port.id, time));
                        dispatch.remove(readyScheduleFlow);
                        stay = false;
                        break;
                    }
                }

            }

            // list最多只有能maxDispatch个， 超出的需要放入dispatch中进行调度
            while(list.size() > maxDispatch){
                Flow poll = list.poll();
                dispatch.add(poll);
            }
            int n = list.size();

            // 计算当前每个端口 的排队队列还能加入几个
            int queueCanIn = 0;
            for (Port port : portList) {
                queueCanIn+= maxQueue -port.scheduleQueue.size;
            }

            // 这是必须要丢掉的流集合
            ArrayList<Flow> abandonFlowList = new ArrayList<>();

            if(choose == 0){
                // 选出最应该被丢弃的流 加入abandonFlowList，这些流不需要安排进入运行队伍
                while(dispatch.size() +n- maxDispatch - queueCanIn>0){
                    Flow abandon = chooseAbandonFlow(0);
                    abandonFlowList.add(abandon);
                    dispatch.remove(abandon);
                }

                // 分配端口
                while(dispatch.size() + n>maxDispatch){
                    Flow first = dispatch.peek();
                    ChoosePort choosePort = choosePort(first,choosePortType);
                    Port port = choosePort.port;
                    if(port == null){
                        break;
                    }
                    port.scheduleQueue.queue.add(first);
                    port.scheduleQueue.size++;
                    schedulesList.add(new Schedule(first.id, port.id, time));
                    dispatch.remove(first);
                }
                // 丢弃abandonFlowList中的流
                for (Flow abandon : abandonFlowList) {
                    abandonTime += abandon.spendTime*2;
                    schedulesList.add(new Schedule(abandon.id, maxPort.id, time));
                }

                // 如果此时还是调度区数量仍然超出，继续丢
                while(dispatch.size() +n> maxDispatch){
                    Flow abandon = chooseAbandonFlow(0);
                    dispatch.remove(abandon);
                    abandonTime += abandon.spendTime*2;
                    schedulesList.add(new Schedule(abandon.id, maxPort.id, time));
                }

                // list中的流放入调度区中
                for (Flow flow1 : list) {
                    dispatch.add(flow1);
                }
            }
            else{

                if(flowIndex >= flowList.size() - maxDispatch - maxQueue*portNum){
                    // 从这里开始的流不会再影响后续的丢弃，所以先安排时间比较长的进入队列,这样可以减少最后任务运行时间
                    while(dispatch.size()+n- maxDispatch - queueCanIn>0){
                        Flow abandon = chooseAbandonFlow(1);
                        abandonFlowList.add(abandon);
                        dispatch.remove(abandon);
                    }

                    for (Flow dispatch1 : dispatch) {
                        list.add(dispatch1);
                    }
                    ArrayList<Flow> temp = new ArrayList<>(list);
                    dispatch.clear();
                    // 从花时间最长的开始安排进入队列
                    Collections.sort(temp,((o1, o2) -> o2.spendTime - o1.spendTime));
                    //  使得调度区一直都是满的，最少需要安排的流
                    int num = temp.size() + n - maxDispatch;
                    for (Flow flow1 : temp) {
                        if(num ==0) break;
                        ChoosePort choosePort = choosePort(flow1,0);
                        Port port = choosePort.port;
                        if(port == null){
                            continue;
                        }
                        num--;
                        list.remove(flow1);
                        port.scheduleQueue.queue.add(flow1);
                        port.scheduleQueue.size++;
                        schedulesList.add(new Schedule(flow1.id, port.id, time));
                    }

                    // 丢弃abandonFlowList中的流
                    for (Flow abandon : abandonFlowList) {
                        abandonTime += abandon.spendTime*2;
                        schedulesList.add(new Schedule(abandon.id, maxPort.id, time));
                    }


                    for (Flow flow1 : list) {
                        dispatch.add(flow1);
                    }
                    // 如果仍然超过调度区容量继续丢
                    while(dispatch.size() > maxDispatch){
                        Flow abandon = chooseAbandonFlow(1);
                        dispatch.remove(abandon);
                        abandonTime += abandon.spendTime*2;
                        schedulesList.add(new Schedule(abandon.id, maxPort.id, time));
                    }


                }
                else{
                    // 流前中段
                    while(dispatch.size() +n- maxDispatch - queueCanIn>0){
                        Flow abandon = chooseAbandonFlow(0);
                        abandonFlowList.add(abandon);
                        dispatch.remove(abandon);
                    }
                    // 分配端口
                    while(dispatch.size() + n>maxDispatch){
                        Flow first = dispatch.peek();
                        ChoosePort choosePort = choosePort(first,choosePortType);
                        Port port = choosePort.port;
                        if(port == null){
                            break;
                        }
                        port.scheduleQueue.queue.add(first);
                        port.scheduleQueue.size++;
                        schedulesList.add(new Schedule(first.id, port.id, time));
                        dispatch.remove(first);
                    }
                    // 丢弃abandonFlowList中的流
                    for (Flow abandon : abandonFlowList) {
                        abandonTime += abandon.spendTime*2;
                        schedulesList.add(new Schedule(abandon.id, maxPort.id, time));
                    }

                    // 丢弃流
                    while(dispatch.size() +n> maxDispatch){
                        Flow abandon = chooseAbandonFlow(0);
                        dispatch.remove(abandon);
                        abandonTime += abandon.spendTime*2;
                        schedulesList.add(new Schedule(abandon.id, maxPort.id, time));
                    }
                    for (Flow flow1 : list) {
                        dispatch.add(flow1);
                    }

                }
            }
            time++;
        }


        // 把剩余在分配区的流加入到端口队列中
        while(dispatch.size()>0){
            for (Port port : portList) {
                // 查看是否有运行结束的
                if(!port.scheduleQueue.endTimeMap.isEmpty()){
                    Integer firstEndTime = port.scheduleQueue.endTimeMap.firstEntry().getKey();
                    if( firstEndTime <= time){
                        TreeMap<Integer, List<Flow>> endTimeMap = port.scheduleQueue.endTimeMap;
                        List<Flow> flows = endTimeMap.firstEntry().getValue();
                        for (Flow flow1 : flows) {
                            port.scheduleQueue.runEndBandwidth += flow1.bandwidth;
                        }
                        port.scheduleQueue.endTimeMap.remove(firstEndTime);
                    }
                }
                // 排队区中是否有可以进入运行队列的
                while(port.scheduleQueue.size>0 &&port.scheduleQueue.runEndBandwidth >= port.scheduleQueue.queue.get(0).bandwidth){
                    port.scheduleQueue.runEndBandwidth -=  port.scheduleQueue.queue.get(0).bandwidth;
                    Flow remove = port.scheduleQueue.queue.remove(0);
                    List<Flow> flows = port.scheduleQueue.endTimeMap.getOrDefault(time + remove.spendTime, new ArrayList<>());
                    flows.add(remove);
                    port.scheduleQueue.endTimeMap.put(time + remove.spendTime,flows);
                    port.scheduleQueue.size--;
                }
            }


            // 最后分配所有的流，从时间从高到低进行安排
            Queue<Flow> lastFlows = new PriorityQueue<Flow>(((o1, o2) -> {
                if(o2.spendTime == o1.spendTime){
                    return o1.bandwidth -o2.bandwidth;
                }
                return o2.spendTime - o1.spendTime;
            }));
            for (Flow flow : dispatch) {
                lastFlows.add(flow);
            }
            while(lastFlows.size() >0){
                Flow first = lastFlows.peek();
                ChoosePort choosePort = choosePort(first, 0);
                Port port = choosePort.port;
                if(port == null){
                    break;
                }
                port.scheduleQueue.queue.add(first);
                port.scheduleQueue.size++;
                schedulesList.add(new Schedule(first.id, port.id, time));
                lastFlows.remove(first);
                dispatch.remove(first);
            }
            time++;
        }


        // 计算所有端口传输完流所需要的时间
        for (Port port : portList) {
            // 清空端口的排队区
            while (port.scheduleQueue.size > 0) {
                Flow flow = port.scheduleQueue.queue.get(0);
                int startTime = 0;
                while(port.scheduleQueue.runEndBandwidth < flow.bandwidth){
                    Map.Entry<Integer, List<Flow>> firstEntry = port.scheduleQueue.endTimeMap.pollFirstEntry();
                    List<Flow> list = firstEntry.getValue();
                    startTime = firstEntry.getKey();
                    for (Flow flow1 : list) {
                        port.scheduleQueue.runEndBandwidth += flow1.bandwidth;
                    }
                }
                while(port.scheduleQueue.size >0  && port.scheduleQueue.runEndBandwidth >= port.scheduleQueue.queue.get(0).bandwidth){
                    flow = port.scheduleQueue.queue.get(0);
                    port.scheduleQueue.runEndBandwidth -= flow.bandwidth;
                    List<Flow> list = port.scheduleQueue.endTimeMap.getOrDefault(startTime + flow.spendTime, new ArrayList<>());
                    list.add(flow);
                    port.scheduleQueue.endTimeMap.put(startTime+flow.spendTime, list);
                    port.scheduleQueue.queue.remove(flow);
                    port.scheduleQueue.size--;
                }
            }
            // 获取最后一个结束时间
            if(port.scheduleQueue.endTimeMap.size()>0){
                Integer lastTime = port.scheduleQueue.endTimeMap.lastKey();
                runrunTime = Math.max(lastTime,runrunTime);
            }
        }
        
    }


    static List<Flow> getFlowList(String filePath) throws IOException {
        List<Flow> flowList = new ArrayList<Flow>();
        String fileName = filePath+"/flow.txt";
        BufferedReader in = new BufferedReader(new FileReader(fileName));
        String str;
        in.readLine();
        while ((str = in.readLine()) != null) {
            String[] strings = str.split(",");
            Flow flow = new Flow(Integer.valueOf(strings[0]), Integer.valueOf(strings[1]), Integer.valueOf(strings[2]), Integer.valueOf(strings[3]) );
            flowList.add(flow);
        }

        return  flowList;
    }

    static List<Port> getPortList(String filePath) throws IOException {
        List<Port> portList = new ArrayList<Port>();
        String fileName = filePath+"/port.txt";

        BufferedReader in = new BufferedReader(new FileReader(fileName));
        String str;
        in.readLine();
        while ((str = in.readLine()) != null) {
            String[] strings = str.split(",");
            Port port = new Port(Integer.valueOf(strings[0]), Integer.valueOf(strings[1]));
            portList.add(port);
        }
        return  portList;
    }

    static ChoosePort choosePort(Flow flow , int type ){
        Port ansPort = null;
        int ansTime = Integer.MAX_VALUE;
        int maxBandwidth = -1;
        int remainBandwidth = Integer.MAX_VALUE;
        for (Port port : portList) {
            if(port.bandwidth < flow.bandwidth || port.scheduleQueue.size >= maxQueue) continue;
            List<Flow> queue = new ArrayList<>(port.scheduleQueue.queue);
            TreeMap<Integer, List<Flow>> runningMap = new TreeMap<>(port.scheduleQueue.endTimeMap);
            int runEndBandwidth = port.scheduleQueue.runEndBandwidth;
            int time = -1;
            for (Flow queueFlow : queue) {
                while(runningMap.size()>0  || runEndBandwidth < queueFlow.bandwidth){
                    Map.Entry<Integer, List<Flow>> firstEntry = runningMap.firstEntry();
                    List<Flow> flows = firstEntry.getValue();
                    time = firstEntry.getKey();
                    for (Flow flow1 : flows) {
                        runEndBandwidth+= flow1.bandwidth;
                    }
                    runningMap.pollFirstEntry();
                }
               if(runEndBandwidth >= queueFlow.bandwidth){
                   int endTime = time+ queueFlow.spendTime;
                   List<Flow> list = runningMap.getOrDefault(endTime, new ArrayList<>());
                   list.add(queueFlow);
                   runningMap.put(endTime, list);
                   runEndBandwidth -= queueFlow.bandwidth;
               }

            }
            while(runningMap.size()>0 &&  runEndBandwidth < flow.bandwidth){
                Map.Entry<Integer, List<Flow>> firstEntry = runningMap.firstEntry();
                List<Flow> flows = firstEntry.getValue();
                time = firstEntry.getKey();
                for (Flow flow1 : flows) {
                    runEndBandwidth+= flow1.bandwidth;
                }
                runningMap.pollFirstEntry();
            }
            switch (type){
                case 0:
                    // 最佳适应法
                    if( runEndBandwidth >= flow.bandwidth ){
                        if(runEndBandwidth - flow.bandwidth < remainBandwidth){
                            remainBandwidth = runEndBandwidth - flow.bandwidth;
                            ansPort = port;
                            ansTime = time;
                        }else if(runEndBandwidth - flow.bandwidth == remainBandwidth){
                            if(time < ansTime){
                                remainBandwidth = runEndBandwidth - flow.bandwidth;
                                ansPort = port;
                                ansTime = time;
                            }
                        }
                    }
                    break;
                case 1:
                    // 加入这个任务，使得这个任务结束时能腾出最多的空间
                    if( runEndBandwidth >= flow.bandwidth ){
                        int sumBandwidth = 0;
                        List<Flow> list = runningMap.getOrDefault(time + flow.spendTime, new ArrayList<>());
                        for (Flow flow1 : list) {
                            sumBandwidth+= flow1.bandwidth;
                        }
                        if(sumBandwidth>maxBandwidth){
                            ansPort = port;
                            ansTime = time;
                            maxBandwidth = sumBandwidth;
                        }
                    }
                    break;
                default:
                    // 最早开始任务
                    if( runEndBandwidth >= flow.bandwidth && (time < ansTime || (time == ansTime && runEndBandwidth - flow.bandwidth < remainBandwidth)) ){
                        ansPort = port;
                        ansTime = time;
                        remainBandwidth = runEndBandwidth - flow.bandwidth;
                    }
                    break;
            }

        }
        return  new ChoosePort(ansPort,ansTime);

    }



    static Flow chooseAbandonFlow(int type ){
        Flow abandon = dispatch.peek();
        switch (type){
            case 0:
                //  综合选择时间短，带宽长的流丢弃
                for (Flow flow : dispatch) {
                   if( (flow.bandwidth/(flow.spendTime*Math.log(flow.spendTime)*Math.log(flow.spendTime)) )       >      (abandon.bandwidth/ (abandon.spendTime*Math.log(abandon.spendTime)*Math.log(abandon.spendTime))) ){
                        abandon = flow;
                    }
                }
                break;
            default:
                // 选择 时间短的丢弃
                for (Flow flow : dispatch) {
                    if(flow.spendTime < abandon.spendTime){
                        abandon  = flow;
                    }else if ( flow.spendTime == abandon.spendTime && flow.bandwidth > abandon.bandwidth){
                        abandon = flow;
                    }
                }
                break;
        }

       return abandon;

    }

    static class Flow{
        int id;
        int bandwidth;
        int startTime;
        int spendTime;
        public Flow(int id, int bandwidth, int startTime, int spendTime){
            this.id = id;
            this.bandwidth = bandwidth;
            this.startTime = startTime;
            this.spendTime = spendTime;
        }

        public Flow() {
        }

        @Override
        public String toString() {
            return "Flow{" +
                    "id=" + id +
                    ", bandwidth=" + bandwidth +
                    ", startTime=" + startTime +
                    ", spendTime=" + spendTime +
                    '}';
        }
    }
    static class Port{
        int id;
        int bandwidth;
        ScheduleQueue scheduleQueue;
        public Port(int id, int bandwidth) {
            this.id = id;
            this.bandwidth = bandwidth;
            this.scheduleQueue = new ScheduleQueue(bandwidth);
        }

        @Override
        public String toString() {
            return "Port{" +
                    "id=" + id +
                    ", bandwidth=" + bandwidth +
                    ", scheduleQueue=" + scheduleQueue +
                    '}';
        }
    }

    static class ScheduleQueue{
        // 可分配带宽
        int runEndBandwidth = -1;
        // 排队数量
        int size = 0;
        //队列
        List<Flow> queue =new LinkedList<>();
        // 正在运行的流，按照结束时间从早到晚排序
        TreeMap<Integer,List<Flow>> endTimeMap = new TreeMap<>();

        public ScheduleQueue(int runEndBandwidth) {
            this.runEndBandwidth = runEndBandwidth;
        }

        @Override
        public String toString() {
            return "ScheduleQueue{" +
                    "runEndBandwidth=" + runEndBandwidth +
                    ", size=" + size +
                    ", queue=" + queue +
                    ", endTimeMap=" + endTimeMap +
                    '}';
        }
    }
    static class Schedule{
       int flowId;
       int portId;
       int time;

        public Schedule(int flowId, int portId, int time) {
            this.flowId = flowId;
            this.portId = portId;
            this.time = time;
        }

        @Override
        public String toString() {
            return "Schedule{" +
                    "flowId=" + flowId +
                    ", portId=" + portId +
                    ", time=" + time +
                    '}';
        }
    }

    static  class ChoosePort{
        Port port;
        int time;

        public ChoosePort(Port port, int time) {
            this.port = port;
            this.time = time;
        }
    }
}
